/*
Copyright 2025 PerfXLab (Beijing) Technologies Co., Ltd.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "OpenSparseBLAS_func.h"
#include <iostream>
#include <complex>
#include <vector>
#include <cassert>
// -----------------------------------------------------------------------------
// 通用 CSR 对称(复对称)矩阵 × 向量： y = A * x
//   uplo: OPENSPBLAS_UPPER / OPENSPBLAS_LOWER
// -----------------------------------------------------------------------------
template <typename T>
void OpenSpB_csrsymv(
    OpenSpB_uplo_type uplo,
    OpenSpB_base_type index_base,
    int m,
    const T* a,
    const int* ia,
    const int* ja,
    const T* x,
    T* y
)
{
    if (m <= 0 || !a || !ia || !ja || !x || !y) {
        std::cerr << "error: illegal input (dimensions or null pointer)\n";
        return;
    }
    if (!(index_base == OPENSPBLAS_ZERO_BASE || index_base == OPENSPBLAS_ONE_BASE)) {
        std::cerr << "error: unsupported index_base (must be 0- or 1-based)\n";
        return;
    }

    const int base = (index_base == OPENSPBLAS_ONE_BASE) ? 1 : 0;
    assert(ia[0] == base && "ia[0] must be 0 (zero-based) or 1 (one-based)");

    const bool use_upper = (uplo == OPENSPBLAS_UPPER);
    const bool use_lower = (uplo == OPENSPBLAS_LOWER);
    if (!use_upper && !use_lower) {
        std::cerr << "error: uplo must be OPENSPBLAS_UPPER or OPENSPBLAS_LOWER\n";
        return;
    }

    for (int i = 0; i < m; ++i) y[i] = T{};

    for (int i = 0; i < m; ++i)
    {
        const int row_start = ia[i]   - base;
        const int row_end   = ia[i+1] - base;

        for (int p = row_start; p < row_end; ++p)
        {
            const int j = ja[p] - base;
            assert(j >= 0 && j < m);

            // 仅累加与 uplo 一致的半区，避免重复
            if (use_upper && j < i) continue;
            if (use_lower && j > i) continue;

            const T aij = a[p];

            y[i] += aij * x[j];
            if (j != i) y[j] += aij * x[i];
        }
    }
}

// -----------------------------------------------------------------------------
// s / d / c / z 外壳（与 cscmm 风格一致）
// -----------------------------------------------------------------------------
void OpenSpB_scsrsymv(
    OpenSpB_uplo_type uplo,
    OpenSpB_base_type index_base,
    const int* m,
    const float* a,
    const int* ia,
    const int* ja,
    const float* x,
    float* y)
{
    OpenSpB_csrsymv<float>(
        uplo, index_base, *m, a, ia, ja, x, y
    );
}

void OpenSpB_dcsrsymv(
    OpenSpB_uplo_type uplo,
    OpenSpB_base_type index_base,
    const int* m,
    const double* a,
    const int* ia,
    const int* ja,
    const double* x,
    double* y)
{
    OpenSpB_csrsymv<double>(
        uplo, index_base, *m, a, ia, ja, x, y
    );
}

void OpenSpB_ccsrsymv(
    OpenSpB_uplo_type uplo,
    OpenSpB_base_type index_base,
    const int* m,
    const void* a,
    const int* ia,
    const int* ja,
    const void* x,
    void* y)
{
    OpenSpB_csrsymv<std::complex<float>>(
        uplo, index_base, *m,
        static_cast<const std::complex<float>*>(a),
        ia,
        ja,
        static_cast<const std::complex<float>*>(x),
        static_cast<std::complex<float>*>(y)
    );
}

void OpenSpB_zcsrsymv(
    OpenSpB_uplo_type uplo,
    OpenSpB_base_type index_base,
    const int* m,
    const void* a,
    const int* ia,
    const int* ja,
    const void* x,
    void* y)
{
    OpenSpB_csrsymv<std::complex<double>>(
        uplo, index_base, *m,
        static_cast<const std::complex<double>*>(a),
        ia,
        ja,
        static_cast<const std::complex<double>*>(x),
        static_cast<std::complex<double>*>(y)
    );
}
